package com.freewater.javabase.demo.reflect.type;

import java.io.Serializable;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.lang.annotation.ElementType.TYPE_PARAMETER;

// 反射类型参数及边界
public class Main {

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {TYPE_PARAMETER})
    @interface KeyTypeDesc {
        String desc();
    }

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {TYPE_PARAMETER})
    @interface ValueTypeDesc {
        String desc();
    }

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {TYPE_PARAMETER})
    @interface FunctionTypeDesc {
        String desc();
    }

    interface Sample<@KeyTypeDesc(desc = "key desc") T extends Serializable,
            @ValueTypeDesc(desc = "val desc") R extends Serializable,
            @FunctionTypeDesc(desc = "func desc") F extends Function<T, R>> {
        R[] doSomething(T k, F f);

        List<? super R> doOther(List<? extends T> list);
    }

    static class SampleImpl implements Sample<Integer,
            String,
            Function<Integer, String>> {

        @Override
        public String[] doSomething(Integer k, Function<Integer, String> f) {
            String[] result = new String[3];
            for (int i = 0; i < 3; i++) {
                result[i] = f.apply(k);
            }
            return result;
        }

        @Override
        public List<? super String> doOther(List<? extends Integer> list) {
            return list.stream()
                    .map((Function<Integer, String>) k -> k == null ? "" : k + "")
                    .collect(Collectors.toList());
        }
    }

    public static void main(String[] args) {
        // 类Type
        System.out.println(Sample.class);
        // 类型参数Type
        System.out.println("Name\tAnnotations\tBounds");
        TypeVariable[] typeParameters = Sample.class.getTypeParameters();
        for (TypeVariable typeParameter : typeParameters) {
            System.out.print(typeParameter + "\t");
            System.out.print(Arrays.toString(typeParameter.getAnnotations()) + "\t");
            System.out.print(Arrays.toString(typeParameter.getBounds()) + "\t");
            System.out.println();
        }
        // 泛型数组Type
        Method[] methods = Sample.class.getMethods();
        Method doSomethingM = Arrays.stream(methods)
                .filter(m -> m.getName().equals("doSomething"))
                .findFirst().get();
        GenericArrayType genericArrayType = (GenericArrayType) doSomethingM.getGenericReturnType();
        System.out.println(genericArrayType);

        // 参数化Type
        System.out.println("SampleImpl use actual type:");
        Type genericInterface = SampleImpl.class.getGenericInterfaces()[0];
        ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        for (Type actualTypeArgument : actualTypeArguments) {
            System.out.println(actualTypeArgument);
        }

        // 通配符Type
        Method doOtherM = Arrays.stream(methods)
                .filter(m -> m.getName().equals("doOther"))
                .findFirst().get();
        ParameterizedType genericReturnType = (ParameterizedType) doOtherM.getGenericReturnType();
        WildcardType wildcardType = (WildcardType) genericReturnType.getActualTypeArguments()[0];
        System.out.println(wildcardType);
    }
}
